package cn.cosmosx.base.helper;

import cn.cosmosx.base.constant.AuthSwitch;
import cn.cosmosx.base.constant.GlobalConstant;
import cn.cosmosx.base.filter.HttpServletRequestWrapperFilter;
import cn.cosmosx.base.util.IPUtil;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 当前线程上下文帮助类
 */
public abstract class CurrentThreadContextHelper {

    /**
     * 请求 HttpServletRequest，
     * 可能为包装过的HttpServletRequestWrapperFilter.RequestBodyWrapper
     */
    public static HttpServletRequest getRequest() {
        return ThreadContext.get(ContextDict.REQUEST);
    }

    /**
     * 响应 HttpServletResponse
     */
    public static HttpServletResponse getResponse() {
        return ThreadContext.get(ContextDict.RESPONSE);
    }

    /**
     * 请求IP地址
     */
    public static String getRequestIP() {
        return ThreadContext.get(ContextDict.REQUEST_IP);
    }

    /**
     * 请求接口
     */
    public static String getRequestApi() {
        return ThreadContext.get(ContextDict.REQUEST_API);
    }

    /**
     * 请求token，通常为Authorization
     */
    public static String getToken() {
        return ThreadContext.get(ContextDict.AUTHORIZATION);
    }

    static String token() {
        for (String k : getHeaders().keySet()) {
            if (GlobalConstant.TOKEN.equalsIgnoreCase(k)) {
                return getHeaders().get(k);
            }
        }
        return null;
    }

    /**
     * 请求cookies
     */
    public static Cookie[] getCookies() {
        return ThreadContext.get(ContextDict.COOKIES);
    }

    /**
     * 请求头参数
     */
    public static Map<String, String> getHeaders() {
        return ThreadContext.get(ContextDict.HEADERS);
    }

    /**
     * 请求参数
     */
    public static Map<String, String> getParams() {
        return ThreadContext.get(ContextDict.REQUEST_PARAMS);
    }

    /**
     * 全局流水号，请求时生成，贯穿本次请求结束，对当前线程有效
     *
     * @return - 全局流水号
     */
    public static String getGlobalId() {
        return ThreadContext.get(ContextDict.GLOBAL_ID);
    }

    public static void init(HttpServletRequest request, HttpServletResponse response) {
        ThreadContext.set(ContextDict.REQUEST, request);
        ThreadContext.set(ContextDict.RESPONSE, response);
        if (request instanceof HttpServletRequestWrapperFilter.RequestBodyWrapper) {
            HttpServletRequestWrapperFilter.RequestBodyWrapper wrapper = (HttpServletRequestWrapperFilter.RequestBodyWrapper) request;
            ThreadContext.set(ContextDict.HEADERS, wrapper.getHeaders());
            ThreadContext.set(ContextDict.REQUEST_PARAMS, wrapper.getParamsMap());
        } else {
            ThreadContext.set(ContextDict.HEADERS, getHeaders(request));
            ThreadContext.set(ContextDict.REQUEST_PARAMS, getParams(request));
        }
        ThreadContext.set(ContextDict.GLOBAL_ID, UUID.randomUUID().toString().replaceAll("-", ""));
        ThreadContext.set(ContextDict.REQUEST_IP, IPUtil.getRealIP(request));
        ThreadContext.set(ContextDict.REQUEST_API, AuthSwitch.getRequestPath(request));
        ThreadContext.set(ContextDict.AUTHORIZATION, token());
        ThreadContext.set(ContextDict.COOKIES, request.getCookies());
    }

    static Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> headers = new LinkedHashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerKey = headerNames.nextElement().toLowerCase();
            String headerValue = request.getHeader(headerKey);
            headers.put(headerKey, headerValue);
        }
        return headers;
    }

    static Map<String, String> getParams(HttpServletRequest request) {
        Map<String, String> params = new LinkedHashMap<>();
        Enumeration<String> headerNames = request.getParameterNames();
        while (headerNames.hasMoreElements()) {
            String paramKey = headerNames.nextElement().toLowerCase();
            String paramValue = request.getParameter(paramKey);
            params.put(paramKey, paramValue);
        }
        return params;
    }

    public static void clear() {
        ThreadContext.THREAD_CONTEXT.remove();
    }

    private final static class ThreadContext {
        // 此类扩展ThreadLocal以提供从父线程到子线程的值继承：创建子线程时，子线程接收父线程具有值的所有可继承线程局部变量的初始值。
        private final static ThreadLocal<Map<Object, Object>> THREAD_CONTEXT = new InheritableThreadLocal<>();

        public static <K, V> void set(K key, V value) {
            getCurrentThreadMap().put(key, value);
        }

        public static <K, V> V get(K key) {
            return get(key, null);
        }

        public static <K, V> V get(K key, V defaultValue) {
            return Optional.ofNullable((V) getCurrentThreadMap().get(key)).orElse(defaultValue);
        }

        public static Map<Object, Object> getCurrentThreadMap() {
            Map<Object, Object> map = THREAD_CONTEXT.get();
            if (map == null) {
                map = new LinkedHashMap<>();
                THREAD_CONTEXT.set(map);
            }
            return map;
        }
    }

    private enum ContextDict {
        /**
         * 当前请求 request
         */
        REQUEST,
        /**
         * 当前响应 response
         */
        RESPONSE,
        /**
         * 请求头参数
         */
        HEADERS,
        /**
         * 请求参数
         */
        REQUEST_PARAMS,
        /**
         * cookies
         */
        COOKIES,
        /**
         * 请求token
         */
        AUTHORIZATION,
        /**
         * 当前请求来源IP地址
         */
        REQUEST_IP,
        /**
         * 当前请求接口
         */
        REQUEST_API,
        /**
         * 全局流水号
         */
        GLOBAL_ID,
    }

}
